Skip to main content

400. Pre-rendering

What and Why Pre-rendering

What is Pre-rendering

  • By default, Next.js pre-renders every page. This means that Next.js generates HTML for each page in advance, instead of having it all done by client-side JavaScript.
  • Pre-rendering can result in better performance and SEO.

Why Pre-rendering

  • better performance
  • SEO benefit

Types of Rendering

  • Next.js has two forms of pre-rendering: Static Generation and Server-side Rendering. The difference is in when it generates the HTML for a page.
    • Static site generation(SSG) = getStaticProps = data fetched at build time
      • Incremental static site regeneration(ISR)
      • Dynamic static site generation = getStaticPaths
    • Server side rendering = getServerSideProps = data fetched on each request
  • Client side data fetching can be added on both type of rendering

Static Site Generation

  • Next.js get-static-props
  • The HTML with all the data that makes up the content of the web page are generated in advance when you build your application.
  • ==The default pre-rendering method==
  • The recommended method to pre-render pages whenever possible.
  • Can be built once and served cache version from CDN. resulting faster delivery time.
  • variation of SSG
    • without data
    • with data
    • incremental static regeneration
    • dynamic parameters when fetching data
//static generation without data
function Home(){
return <h1>about us page</h1>
}
export default Home;
//static generation with data
function Home({ users }) {
return (
<div>
{users.map((user) => {
return (
<>
<p>
{user.firstName} {user.lastName}{" "}
</p>
</>
);
})}
</div>
);
}

export default Home;

export async function getStaticProps() {
const response = await fetch("https://dummyjson.com/users");
const data = await response.json();

return {
props: {
users: data.users,
},
};
}

  • getStaticProps
    • can write any back-end logic here,
      • ex: accessing file system, DB
    • code her wont't be shipped to browser, only back-end
      • on runs on the back-end
    • only can be used in pages,
      • not in components, client-side or anywhere else.
    • must return an object with props key
    • will run only once during build time: production
      • except on development time. it will run on every request

getStaticProps does not have access to the incoming request (such as query parameters or HTTP headers) as it generates static HTML.

  • Any Link component in the view-port(initially or on scroll) will be prefetched by default including the corresponding data for pages using static site generation.
  • When a page with getStaticProps is pre-rendered at build time, nextjs renders.
    • HTML page,
    • JSON (holding the result of getStaticProps)
    • and the required JS chunk.
  • The JSON file will be used in client-side routing through next/link or next/router.
  • When you navigate to a page that's pre-rendered using getStaticProps, Next.js fetches
    • The JSON file which is pre-computed at build time
      • This JSON is used as a prop to create the page component client-side.

SSG + getStaticPaths

  • If a page has Dynamic Routes and uses getStaticProps, it needs to define a list of paths to be statically generated.
  • When you export a function called getStaticPaths (Static Site Generation) from a page that uses dynamic routes, Next.js will statically pre-render all the paths specified by getStaticPaths.
function UserDetail({user}) {  
//...component ui
}

export default UserDetail;

// generate pre-render for all ids
export async function getStaticPaths() {
const response = await fetch(`https://dummyjson.com/users`);
const data = await response.json();

const paths = data.users.map((user) => ({
params: { userid: `${user.id}` },
}))

return {
paths: paths,
// paths: [{params: {userid: '1'}}, {params: {userid: '2'}}],
fallback: false, // can also be true or 'blocking'
}
}

// generate pre-render for certain ids
export async function getStaticPaths() {

return {
paths: [
{params: {userid: '1'}},
{params: {userid: '2'}},
{params: {userid: '3'}},
],
fallback: false, // can also be true or 'blocking'
}
}

export async function getStaticProps(context) {
const {params} = context;
const response = await fetch(`https://dummyjson.com/users/${params.userid}`);
const data = await response.json();

return {
props: {
user: data,
},
};
}

Fallback Key

  • fallback key is a required value with getstaticPath
  • can have the following possible values
    • false
      • any paths not returned by getStaticPaths, will result in 404 page.
      • when should you use this?
        • if you have a small number of pages pre-render with getStaticPaths
        • when new pages are not added often
        • ex: a blog site with a few articles
    • true
      • instead of return a 404 page, next.js will serve a "fallback" version of the page on the first request to such path.
        • instead in the background, Next.js will statically generate the request path HTML and JSON. This includes running getStaticProps.
        • subsequent request to the same path will be served from the generated pages, just like other pages pre-rendered at build time.
      • When should you use this?
        • if the app has a very large number of static pages that depend on data
        • if you want to statically generate a small subset of pages that are popular and use fallback:true for the rest,
          • think: e-commerce product listing
    • blocking
      • similar to fallback: true, except, nextjs will not serve a "fallback" version of the page on the first request to such path.
        • instead next.js will render that page on the server and return the generated HTML.
        • the browser waits for nextjs to respond and once the HTML and JSON are received it will load the page, with no flash or loading state.
      • When to use this?
        • basically the same reason as fallback: true, except when using blocking you wont see a loading screen
        • to support crawler, so instead of showing a loading page while pre-rendering, we would wait and show the actual page
      • Recommendation
        • Nextjs recommends using fallback: true
        • use fallback: true is you care more for user's UX, and don't have an SEO issue
        • use fallback: 'blocking', is you are having an SEO issue
//fallback true
import {useRouter} from "next/router";

function UserDetail({user}) {
const router = useRouter();

if(router.isFallback){
return "Loading..."; //will be visible when next.js is busy pre-rendering for not-found ids
}

return <h1>{user.firstName} - {user.lastName} - {user.age}</h1>
}

export default UserDetail;

// generate pre-render for certain ids
export async function getStaticPaths() {

return {
paths: [
{params: {userid: '1'}},
{params: {userid: '2'}},
{params: {userid: '3'}},
],
fallback: true,
}
}

export async function getStaticProps(context) {
const {params} = context;
const response = await fetch(`https://dummyjson.com/users/${params.userid}`);
const data = await response.json();

return {
props: {
user: data,
},
};
}

What if Key is Not Found

  • To handle 404 pages, return not found from getStaticProps
    • nextjs by default will return 404 for you
export async function getStaticProps(context) {  
const {params} = context;
const response = await fetch(`https://dummyjson.com/users/${params.userid}`);
const data = await response.json();

//is the result is null or undefined
if(!data.id){
return {
notFound: True,
}
}

return {
props: {
user: data,
},
};
}

Pros and Cons of SSG

  • pros
    • pre-rendered pages can be pushed to CDN, cached and served to users globally instantly.
    • better performance with SEO, as they are fast and HTML pages
  • cons
    • the build time is dependent on the number of pages you have, for large pages it will take significant time,
      • ex:, you have a million users, and are using SSG for profile page
    • data might expire, if you didn't build the app recently stale data

ISR(incremental Static Site regeneration)

  • A solution to SSG cons.
  • With ISR, Next.js allows you to update static pages after you've build your application
  • you can statically generate individual pages without needing to rebuild the entire site, effectively solving the issue of dealing with stale data
  • How to?
    • in the getStaticProps function, apart from the props key, we can specify a revalidate key
    • the value for re-validate is the number of seconds after which a page re-generation can occur
export async function getStaticProps(context) {  
const {params} = context;
const response = await fetch(`https://dummyjson.com/users/${params.userid}`);
const data = await response.json();

//is the result is null or undefined
if(!data.id){
return {
notFound: True,
}
}

return {
props: {
user: data,
},
revalidate: 10, //10 seconds
};
}
  • re-generation doesn't happen exactly after n seconds, but
    • a re-generation is initiated only if a user makes a request after the re-validation time.
    • thus pages that are not frequently access won't be re-generated.
  • the re-generation can also fail and the previous cached HTML could be served till the subsequent re-generation succeed

Programmatic Revalidation

  • You can programmatically invoke regeneration with next.js, for instance; if you have a webhook you can connect it to nextjs api that will invoke a regeneration
  • this pattern is called on-demand-ISR
export default function handler(req,res) {  
//check if proper app initiated this
if(req.query.secret !== process.env.REVALIDATION_PASSWORD ){
return res.status(401).json({msg: 'Invalid token'})
}

try{
await res.unstable_revalidate("/blog"); //we're regernating about page
return res.json({revalidated: true})
}catch(err){
return res.status(500).send('Error revalidating')
}
}